//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#define __NO_LINKNODE_CONSTRUCTOR
#include <mantle.h>
#include <ckernel.h>
#include <core.h>
#include <crust.h>

extern UInt32 g_uShareMemStart;
extern CKernel * g_pCKernel; // define in crust/kmarshal/kmarshal.cpp

#ifndef _RELEASE
extern Boolean g_bDbgStubEntry; //for gdbstub
#endif //_RELEASE

DLinkNode CProcess::s_processList = { &s_processList, &s_processList };
#ifdef DEBUG_KMUTEX
KMutex CProcess::s_processListLock(__FILE__, __LINE__);
#else
KMutex CProcess::s_processListLock;
#endif //DEBUG_KMUTEX
Int32 CProcess::s_nProcessListState = 0;

PInterface CProcess::ObjProbe(
    REIID Self_riid,
    REIID riid)
{
    if (riid == EIID_IInterface) {
        return (PInterface)(IProcess *)(_CProcess *)this;
    }
    else if (riid == EIID_IProcess) {
        return (IProcess *)(_CProcess *)this;
    }
    else if (riid == EIID_IMemoryTrace) {
        return (IMemoryTrace *)(_CMemoryTrace *)this;
    }
    else if (riid == EIID_CLASS_INFO) {
        return (PInterface)&ECLSID_CProcess;
    }
    else if (riid == EIID_INTERFACE_INFO) {
        return (PInterface) &Self_riid;
    }

    return NULL;
}

// Interface functions
//

UInt32 CProcess::AddRef(void)
{
    return m_cRef.Increment();
}

UInt32 CProcess::Release(void)
{
    Int32 lRef = m_cRef.Decrement();

    assert(lRef >= 0);

    if (0 == lRef) {
        delete this;
    }
    return (UInt32)lRef;
}

UInt32 CProcess::Release(Thread *pThread)
{
    DzDisablePreemption();
    Int32 lRef = m_cRef.Decrement();

    assert(lRef >= 0);

    if (0 == lRef) {
        pThread->m_pOwner = NULL;
        EnablePreemption();
        delete this;
    }
    else {
        EnablePreemption();
    }
    return (UInt32)lRef;
}

ECode CProcess::Aggregate(
    /* [in] */ AggregateType type,
    /* [in] */ IInterface* pObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CProcess::GetDomain(
    /* [out] */ IInterface** ppObj)
{
    return E_NOT_IMPLEMENTED;
}

PInterface CProcess::GetObject()
{
    return (IProcess *)this;
}

Int32 ProcessStartEntry(void * pArg)
{
    ECode ec;
    Int32 stackTop;
    CSharedMemory * pCSharedMem = NULL;
    register CProcess *pProcess = (CProcess*)pArg;
    UInt32 handle[2];

    assert(pProcess == ::GetCurrentProcess());
    assert(pProcess->m_pMainThread == ::GetCurrentThread());
    assert(pProcess->m_pMainThread->m_uUserStackBase);

    stackTop = pProcess->m_pMainThread->m_uUserStackBase + USER_STACK_SIZE
            - USER_STACK_PRESERVED_SIZE;

    memset((void *)(stackTop + USER_STACK_PRESERVED_SIZE - TLS_SIZE),
            0, TLS_SIZE);

    ec = DzFindService(L"SysSharedClassInfo", (PInterface *)&pCSharedMem);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    ec = pCSharedMem->Attach(MemoryProtection_Read,
       (Address *)&g_uShareMemStart);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    pCSharedMem->Release();

    Boolean bIsLoaded;
    ec = pProcess->LoadModuleW(pProcess->m_wszName, \
            &stackTop, NULL, 0, ModuleFileType_EXE, \
            &pProcess->m_pMainModule, &bIsLoaded);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    assert(pProcess->m_pMainModule);
    assert(pProcess->m_pMainModule->m_pModule);

    // The function in USER_STARTUP_ENTRY_BASE will call
    // all _DllMainCRTStartup and then call mainCRTStartup
    // which setup in this->LoadModuleW()
    //
    pProcess->m_pMainThread->SetUserEntry(
        (void *)stackTop,
        (void *)USER_STARTUP_ENTRY_BASE);

    InterlockedCompareExchange((Int32*)&pProcess->m_processState,
            ProcessState_Running, ProcessState_Starting);
    pProcess->m_startResult = NOERROR;

    pProcess->m_startEvent.Notify();

    TryToSendReceiveDebugMsg(DEBUGGEE_PROCESS_CREATE, (UInt32)pProcess);

    handle[0] = (UInt32)pProcess->m_pMainThread;
    handle[1] = USER_STARTUP_ENTRY_BASE;
    TryToSendReceiveDebugMsg(DEBUGGEE_THREAD_CREATE, (UInt32)handle);

    return 0;

ErrorExit:
    assert(ProcessState_Finished != pProcess->m_processState);
    pProcess->m_startResult = ec;
    pProcess->m_startEvent.Notify();
    DzGetSystemTime(&pProcess->m_finishTime);
    pProcess->m_processState = ProcessState_Finished;

    if (pCSharedMem) {
        pCSharedMem->Detach(pProcess, TRUE, TRUE);
    }

    pProcess->FreeProcess();

    pProcess->m_threadLock.Lock();
    if (ThreadState_Running == pProcess->m_pMainThread->m_uPoolThreadState) {
        pProcess->m_pMainThread->m_inProcLink.Detach();
        // for detach twice.(In thread release)
        pProcess->m_pMainThread->m_inProcLink.Initialize();
        pProcess->m_nThreadListState++;
        pProcess->m_pMainThread->Release();
        pProcess->m_pMainThread->m_uPoolThreadState = ThreadState_Finished;
    }
    pProcess->m_threadLock.Unlock();

    ExitThread(pProcess);
    assert(0);
    return ec;
}

ECode CProcess::Start(
    /* [in] */ WString name,
    /* [in] */ WString args)
{
    ECode ec;
    Thread *pTempThread;
    CProcess *pCurrentProc;

    if (name.IsNull() || wcslen(name) > (MAXIMUM_FULL_PATH_LENGTH - 1)) {
        return E_INVALID_ARGUMENT;
    }

    if (!args.IsNull() && wcslen(args) > (MAXIMUM_ARGS_LENGTH - 1)) {
        return E_INVALID_ARGUMENT;
    }

    if (ProcessState_Unstarted == m_processState) {
        DzGetSystemTime(&m_startTime);
    }

    if (ProcessState_Unstarted \
        != InterlockedCompareExchange((Int32*)&m_processState,
                ProcessState_Starting, ProcessState_Unstarted)) {
        return E_PROCESS_ALREADY_STARTED;
    }

    ec = m_AS.Initialize();
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    m_wszName = new wchar_t[wcslen(name) + 1];
    if (!m_wszName) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }
    memcpy(m_wszName, name,
        (wcslen(name) + 1) * sizeof(wchar_t));
    if (!args.IsNull()) {
        m_wszArgs = new wchar_t[wcslen(args) + 1];
        if (!m_wszArgs) {
            ec = E_OUT_OF_MEMORY;
            goto ErrorExit;
        }
        memcpy(m_wszArgs, args,
                            (wcslen(args) + 1) * sizeof(wchar_t));
    }

    m_vaBlock = DzAllocKernelPages(
                        GET_UP_ORDER(USER_BLOCK_SIZE / PAGE_SIZE));
    if (!m_vaBlock) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }

    pCurrentProc = GetCurrentProcess();
    if (pCurrentProc) {
        assert(pCurrentProc->m_vaBlock);

        // Copy current process user block to this process.
        //
        memcpy(m_vaBlock, pCurrentProc->m_vaBlock,
            USER_BLOCK_SIZE);
    }
    else {
        // This process is start by kernel, init user block.
        //
        memset(m_vaBlock, 0, USER_BLOCK_SIZE);
    }
    // Map user block to USER_BLOCK_BASE
    if (FALSE == m_AS.m_pHat->LoadMap(
        USER_BLOCK_BASE,
        USER_BLOCK_BASE + USER_BLOCK_SIZE,
        __PA(m_vaBlock),
        MemoryProtection_Read | MemoryProtection_Write,
        TRUE)) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }

    ec = GetThread(&m_pMainThread);
    if (FAILED(ec)) goto ErrorExit;

    CProcess::s_processListLock.Lock();
    m_AS.m_pHat->SyncGlobalHAT();
    CProcess::s_processList.InsertLast((ObjectEnumeratorNode *)this);
    CProcess::s_nProcessListState++;
    CProcess::s_processListLock.Unlock();
    this->AddRef();

    m_pMainThread->SetKernelEntry(ProcessStartEntry, this);

#if defined(_DEBUG)
    wcscpy(m_pMainThread->m_wszName, L"MainThread");
#endif // _DEBUG

    // Thread Start may release Process Object, m_pMainThread may be invalid.
    pTempThread = m_pMainThread;
    pTempThread->Start();
    pTempThread->Release();

    m_startEvent.UninterruptibleWait(NULL);
    return m_startResult;

ErrorExit:
    assert(ProcessState_Finished != m_processState);
    DzGetSystemTime(&m_finishTime);
    m_processState = ProcessState_Finished;

    if (m_wszName) delete[] m_wszName;
    if (m_wszArgs) delete[] m_wszArgs;
    if (m_vaBlock) {
        DzFreeKernelPages(
            m_vaBlock,
            GET_UP_ORDER(USER_BLOCK_SIZE / PAGE_SIZE));
    }
    m_wszName = NULL;
    m_wszArgs = NULL;
    m_vaBlock = NULL;
    return ec;
}

EXTERN KMutex g_marshalLock;

ECode CProcess::Kill()
{
    DLinkNode       *pImportService;
    DLinkNode       *pSharedMem;
    Thread          *pThread;
    uint_t          uPoolThreadState;

    m_bIsCallDllDetach = FALSE;

    if (this == ::GetCurrentProcess()) {
        g_pCKernel->ExitProcess(3);
        assert(0);
    }

    if (m_processState == ProcessState_Unstarted) {
        return E_INVALID_OPERATION;
    }

    if (ProcessState_Finished != m_processState) {
        DzGetSystemTime(&m_finishTime);
    }

    if (ProcessState_Finished \
        == InterlockedExchange((Int32*)&m_processState,
            ProcessState_Finished)) {
        return NOERROR;
    }

    m_startEvent.Notify();
    m_threadLock.Lock();
    // Abort threads
    //
    while (!m_threadList.IsEmpty()) {
        pThread = THIS(
            (InProcessLinkNode *)m_threadList.m_pPrev, Thread, m_inProcLink);

        if (ThreadState_Finished != pThread->m_uPoolThreadState) {
            DzGetSystemTime(&pThread->m_finishTime);
        }

        uPoolThreadState = InterlockedExchange(
            (Int32*)&pThread->m_uPoolThreadState, ThreadState_Finished);
        if (ThreadState_Finished == uPoolThreadState) {
            // pThread being abort.
            m_nThreadListState++;
            m_threadLock.Unlock();
            ::DzSleep(0, NULL);
            m_threadLock.Lock();
            continue;
        }
        pThread->m_inProcLink.Detach();
        // for detach twice.(In thread release)
        pThread->m_inProcLink.Initialize();
        pThread->Abort(this);
        pThread->Release();
    }
    m_nThreadListState++;
    m_threadLock.Unlock();

    m_uStackTop = 0;
    m_moduleLock.Lock();
    if (m_pMainModule) {
        m_pMainModule->Release();
        m_pMainModule = NULL;
        m_moduleList.Detach();
        m_moduleList.Initialize();
    }
    m_moduleLock.Unlock();

    // unregister ezcom commom sources
    //
    g_marshalLock.Lock();
    DeleteObjects(this);
    g_marshalLock.Unlock();

    // if some exceptions occured in process, maybe the
    // m_importServiceList is not empty,
    // we release the Object indicated by oid one by one
    // and destroy the list
    //

    //First, we reanrage the imported object list, make kernel objects is
    //at front of the list and other objects at the tail of the list
    //
    pImportService = m_importServiceList.Next();
    DLinkNode *pTailService = &(m_importServiceList);
    while (pImportService != &(m_importServiceList)) {
        DLinkNode *pTempService = pImportService->Next();
        if (((ImportService *)pImportService)->m_pContext
            == CTX_USER_2_KERN) {
            pImportService->Detach();
            pImportService->Initialize();
            pTailService->InsertNext(pImportService);
            pTailService = pImportService;
        }
        pImportService = pTempService;
    }

    //Second, we release imported objects
    //
    pImportService = m_importServiceList.Next();
    while (pImportService != &(m_importServiceList)) {
        pImportService->Detach();
        DetachServer(((ImportService *)pImportService)->m_oid, \
                    ((ImportService *)pImportService)->m_pContext);
        delete(pImportService);
        pImportService = m_importServiceList.Next();
    }

    // detach shared memory which user have forget to do with or
    // when process have exited abnormally
    //
    pSharedMem = m_attachedMemoryList.Next();
    while (pSharedMem != &(m_attachedMemoryList)) {
        pSharedMem->Detach();
        ((AttachedMemory *)pSharedMem)->m_pSharedMem->Detach(this, FALSE,
            TRUE);
        delete(pSharedMem);
        pSharedMem = m_attachedMemoryList.Next();
    }

    TryToSendReceiveDebugMsg(DEBUGGEE_PROCESS_EXIT, (UInt32)this, this);
    this->FreeProcess();

    return NOERROR;
}

ECode CProcess::WaitForExit(
    /* [in] */ Millisecond32 timeout,
    /* [out] */ WaitResult *pResult)
{
    WaitResult wr;
    if (NULL == pResult) pResult = &wr;

    if (::GetCurrentThread()->m_pOwner == this) {
        return E_INVALID_OPERATION;
    }

    if (ProcessState_Unstarted == m_processState) {
        return E_PROCESS_NOT_STARTED;
    }
    else {
        m_exitEvent.TryWait(DzMillisecondsToTicks(timeout), pResult, NULL);
        return NOERROR;
    }
}

ECode CProcess::IsAlive(Boolean *pAlive)
{
    if (NULL == pAlive) return E_INVALID_ARGUMENT;

    *pAlive = (m_processState == ProcessState_Running);

    return NOERROR;
}

ECode CProcess::SetPriorityRank(
    /* [in] */ ThreadPriorityRank priorityRank)
{
    switch (priorityRank) {
        case ThreadPriorityRank_Normal:
        case ThreadPriorityRank_SoftRealTime:
            break;
        case ThreadPriorityRank_HardRealTime:
            return E_NOT_IMPLEMENTED;
        default:
            return E_INVALID_ARGUMENT;
    }

    if (ProcessState_Finished == m_processState) {
        return E_PROCESS_ALREADY_EXITED;
    }

    m_priorityRank = priorityRank;

    return NOERROR;
}

ECode CProcess::GetPriorityRank(
    /* [out] */ ThreadPriorityRank * pPriorityRank)
{
    if (NULL == pPriorityRank) return E_INVALID_ARGUMENT;

    if (ProcessState_Finished == m_processState) {
        return E_PROCESS_ALREADY_EXITED;
    }

    *pPriorityRank = m_priorityRank;

    return NOERROR;
}

ECode CProcess::SetThreadPoolCapacity(
    /* [in] */ Int32 capacity)
{
    m_threadPool.SetCapacity(capacity);
    return NOERROR;
}

ECode CProcess::GetThreadPoolState(
    /* [out] */ Int32 * pCapacity,
    /* [out] */ Int32 * pThreads)
{
    if (NULL == pCapacity && NULL == pThreads) return E_INVALID_ARGUMENT;

    if (pCapacity) *pCapacity = m_threadPool.m_uCapacity;
    if (pThreads)  *pThreads  = m_threadPool.m_cThreads;
    return NOERROR;
}

ECode CProcess::GetAllThreads(
    /* [out] */ IObjectEnumerator **ppThreads)
{
    IObjectEnumerator *pEnum;

    if (NULL == ppThreads) return E_INVALID_ARGUMENT;

    pEnum = new CObjectEnumerator(
        &m_threadList,
        &m_threadLock,
        &m_nThreadListState,
        (IProcess *)this);
    if (!pEnum) return E_OUT_OF_MEMORY;

    *ppThreads = pEnum;
    pEnum->AddRef();
    return NOERROR;
}

ECode CProcess::GetAllModules(
    /* [out] */ IObjectEnumerator **ppModules)
{
    IObjectEnumerator *pEnum;

    if (NULL == ppModules) return E_INVALID_ARGUMENT;

    pEnum = new CObjectEnumerator(
        &m_moduleList,
        &m_moduleLock,
        &m_nModuleListState,
        (IProcess *)this);
    if (!pEnum) return E_OUT_OF_MEMORY;

    *ppModules = pEnum;
    pEnum->AddRef();
    return NOERROR;
}

ECode CProcess::GetModuleByName(
    /* [in] */ WString name,
    /* [out] */ IModule ** ppMod)
{
    ModuleView *pModuleView;

    if (name.IsNull() || ppMod == NULL) {
        return E_INVALID_ARGUMENT;
    }

    m_moduleLock.Lock();

    Boolean bIsAlive;
    IsAlive(&bIsAlive);
    if (!bIsAlive) {
        m_moduleLock.Unlock();
        return E_PROCESS_NOT_ACTIVE;
    }

    ForEachDLinkNode(ModuleView *, pModuleView, &m_moduleList) {
        if (!_wcsicmp(name, pModuleView->m_pModule->m_wszName)) {
            DzDisablePreemption();
            if (0 == pModuleView->m_cRef) {
                EnablePreemption();
                do {
                    DzSleep(1, NULL);
                } while (ModViewState_Deleted != pModuleView->m_modviewstate);
                ModuleView *pDelModView = pModuleView;
                pModuleView = (ModuleView*)pModuleView->Prev();
                pDelModView->Detach();
                delete pDelModView;
            }
            else {
                pModuleView->AddRef();
                EnablePreemption();
                *ppMod = pModuleView;
                m_moduleLock.Unlock();
                return NOERROR;
            }
        }
    }
    m_moduleLock.Unlock();

    return E_DOES_NOT_EXIST;
}

ECode CProcess::GetModuleByAddress(
    /* [in] */ Address address,
    /* [out] */ IModule * * ppMod)
{
    ModuleView *pModuleView;

    if (ppMod == NULL) return E_INVALID_ARGUMENT;

    m_moduleLock.Lock();

    Boolean bIsAlive;
    IsAlive(&bIsAlive);
    if (!bIsAlive) {
        m_moduleLock.Unlock();
        return E_PROCESS_NOT_ACTIVE;
    }

    ForEachDLinkNode(ModuleView *, pModuleView, &m_moduleList) {
        if (FALL_INTO(address, pModuleView->m_pModule->m_uImageBase,
                pModuleView->m_pModule->m_uTotalSize)) {
            DzDisablePreemption();
            if (0 == pModuleView->m_cRef) {
                EnablePreemption();
                do {
                    DzSleep(1, NULL);
                } while (ModViewState_Deleted != pModuleView->m_modviewstate);
                ModuleView *pDelModView = pModuleView;
                pModuleView = (ModuleView*)pModuleView->Prev();
                pDelModView->Detach();
                delete pDelModView;
            }
            else {
                pModuleView->AddRef();
                EnablePreemption();
                *ppMod = pModuleView;
                m_moduleLock.Unlock();
                return NOERROR;
            }
        }
    }
    m_moduleLock.Unlock();

    return E_DOES_NOT_EXIST;
}

ECode CProcess::GetId(
    /* [out] */ ProcessId * pPid)
{
    if (NULL == pPid) return E_INVALID_ARGUMENT;

    *pPid = ((Int32)this) & 0x7FFFFFFF;
    return NOERROR;
}

ECode CProcess::GetStartInfo(
    /* [out] */ WStringBuf * pName,
    /* [out] */ WStringBuf * pArgs)
{
    if ((pName == NULL || !pName->GetCapacity()) &&
        (pArgs == NULL || !pArgs->GetCapacity())) {
        return E_INVALID_ARGUMENT;
    }

    if (ProcessState_Unstarted == m_processState) {
        return E_PROCESS_NOT_STARTED;
    }

    if (m_wszName && pName && pName->GetCapacity()) {
        pName->Copy(m_wszName);
    }

    if (m_wszArgs && pArgs && pArgs->GetCapacity()) {
        pArgs->Copy(m_wszArgs);
    }

    return NOERROR;
}

ECode CProcess::GetExitCode(
    /* [out] */ Int32 * pExitCode)
{
    if (NULL == pExitCode) return E_INVALID_ARGUMENT;

    if (ProcessState_Finished != m_processState) {
        return E_PROCESS_NOT_EXITED;
    }

    *pExitCode = m_nExitCode;
    return NOERROR;
}

ECode CProcess::GetStartTime(
    /* [out] */ SystemTime * pTime)
{
    if (NULL == pTime) return E_INVALID_ARGUMENT;

    if (ProcessState_Unstarted == m_processState) {
        return E_PROCESS_NOT_STARTED;
    }

    *pTime = m_startTime;
    return NOERROR;
}

ECode CProcess::GetExitTime(
    /* [out] */ SystemTime * pTime)
{
    if (NULL == pTime) return E_INVALID_ARGUMENT;

    if (ProcessState_Finished != m_processState) {
        return E_PROCESS_NOT_EXITED;
    }

    *pTime = m_finishTime;
    return NOERROR;
}

ECode CProcess::GetProcessorTimes(
    /* [out] */ Millisecond32 * pUserTime,
    /* [out] */ Millisecond32 * pKernelTime)
{
    if (NULL == pUserTime
            && NULL == pKernelTime)
        return E_INVALID_ARGUMENT;

    if (NULL != pUserTime) {
        *pUserTime = DzTicksToMilliseconds(m_uUserProcessorTicks);
    }

    if (NULL != pKernelTime) {
        *pKernelTime =
            DzTicksToMilliseconds(m_uPrivilegedProcessorTicks);
    }

    return NOERROR;
}

ECode CProcess::GetMemorySizes(
    /* [out] */ Int32 * pPrivateVirtualMemorySize,
    /* [out] */ Int32 * pSharedVirtualMemorySize,
    /* [out] */ Int32 * pPrivatePhysicalMemorySize,
    /* [out] */ Int32 * pSharedPhysicalMemorySize)
{
    if (NULL == pPrivateVirtualMemorySize
        && NULL == pSharedVirtualMemorySize
        && NULL == pPrivatePhysicalMemorySize
        && NULL == pSharedPhysicalMemorySize) {
        return E_INVALID_ARGUMENT;
    }

    ECode ec;

    if (ProcessState_Running != m_processState) goto Exit;

    ec = m_AS.GetMemorySizes(
            pPrivateVirtualMemorySize,
            pSharedVirtualMemorySize,
            pPrivatePhysicalMemorySize,
            pSharedPhysicalMemorySize);

    return ec;

Exit:
    if (NULL != pPrivateVirtualMemorySize) *pPrivateVirtualMemorySize = 0;
    if (NULL != pSharedVirtualMemorySize) *pSharedVirtualMemorySize = 0;
    if (NULL != pPrivatePhysicalMemorySize) *pPrivatePhysicalMemorySize = 0;
    if (NULL != pSharedPhysicalMemorySize) *pSharedPhysicalMemorySize = 0;
    return NOERROR;
}

// member functions
//
ECode CProcess::LoadModuleW(
    const wchar_t *wszName,
    Int32 *pStackTop,
    BufferOf<Address> * pDllEntries,
    Int32 dwFlags,
    ModuleFileType modFileType,
    ModuleView ** ppModuleView,
    Boolean *pNewlyLoaded)
{
    CModule * pModule;
    ECode ec;
    Int32 tempStack = 0;
    ModuleView * pModView;
    Int32 * pBuffer = NULL;
    Int32 nNewModuleViews = 0;
    Int32 n;

    assert(0 == dwFlags);   // dwFlags is reserved
    assert(ppModuleView != NULL);

    m_moduleLock.Lock();

    pModView = this->FindModuleView(wszName);
    if (pModView) {
        assert(ModuleFileType_DLL == modFileType);
        if (pModView == m_pMainModule) {
            pModView->Release();
            // User try to load main module again
            m_moduleLock.Unlock();
            *pNewlyLoaded = FALSE;
            return E_BAD_DLL_FORMAT;
        }
        else {
            *ppModuleView = pModView;
            // Already call AddRef() in FindModuleView.
            m_moduleLock.Unlock();
            *pNewlyLoaded = FALSE;
            return NOERROR;
        }
    }

    ec = GLoadModuleW(wszName, dwFlags, modFileType, &pModule);
    if (NOERROR != ec) {
        m_moduleLock.Unlock();
        *pNewlyLoaded = FALSE;
        return ec;
    }

    if (pStackTop) tempStack = *pStackTop;
    ec = this->CreateModuleView(
                    pModule, pStackTop, pDllEntries, ppModuleView);
    pModule->Release();
    if (NOERROR != ec) {
        m_moduleLock.Unlock();
        *pNewlyLoaded = FALSE;
        return ec;
    }

    if (pStackTop) {
        PUSH_ARG(*pStackTop, NULL);                // end of startup entry
        PUSH_ARG(*pStackTop, DLL_PROCESS_ATTACH);  // dwReason
        PUSH_ARG(*pStackTop, tempStack - 8);       // point to first
                                                   // startup entry
        *pStackTop -= 4;                           // skip return address
    }

    for (pModView = *ppModuleView; pModView != (ModuleView *)&m_moduleList;
            pModView = (ModuleView *)pModView->Next()) {
        nNewModuleViews++;
    }

    assert(0 != nNewModuleViews);

    if (nNewModuleViews > 10) {
        pBuffer = (Int32 *)malloc(nNewModuleViews * sizeof(Int32));
    }
    else {
        pBuffer = (Int32 *)_alloca(nNewModuleViews * sizeof(Int32));
    }

    if (NULL != pBuffer) {
        for (pModView = *ppModuleView, n = 0;
                pModView != (ModuleView *)&m_moduleList;
                pModView = (ModuleView *)pModView->Next(), n++) {
            pBuffer[n] = (Int32)pModView->m_pModule;
        }
    }

    m_nModuleListState++;
    m_moduleLock.Unlock();

    if (NULL != pBuffer) {
        for (n = nNewModuleViews - 1; n >= 0; n--) {
            TryToSendReceiveDebugMsg(DEBUGGEE_LOAD_DLL, pBuffer[n]);
        }
        if (nNewModuleViews > 10) {
            free(pBuffer);
        }
    }

    *pNewlyLoaded = TRUE;
    return NOERROR;
}

ECode CProcess::CreateModuleView(
    CModule * pModule,
    Int32 *pStackTop,
    BufferOf<Address> * pDllEntries,
    ModuleView ** ppModuleView)
{
    ECode ec;
    register ModuleRef * pModRef;
    ModuleView * pCurrentView, * pView;
    ModuleViewRef * pViewRef;

    pCurrentView = new ModuleView(pModule, this);
    if (!pCurrentView) return E_OUT_OF_MEMORY;

    //
    // Note: Do not invoke pModule->AddRef() here, because the reference
    // should be increased by invoker.
    //
    m_moduleList.InsertLast(pCurrentView);

    ec = this->MapModule(pModule);
    if (NOERROR != ec) goto ErrorExit;

    ForEachDLinkNode(ModuleRef *, pModRef, &(pModule->m_rModuleList)) {
        pView = this->FindModuleView(pModRef->m_pModule);
        if (!pView) {
            ec = this->CreateModuleView(
                        pModRef->m_pModule, pStackTop, pDllEntries, &pView);
            if (NOERROR != ec) goto ErrorExit;
            pModRef->m_pModule->AddRef();
        }

        pViewRef = new ModuleViewRef(pView);
        if (!pViewRef) {
            pView->Release();
            ec = E_OUT_OF_MEMORY;
            goto ErrorExit;
        }
        pCurrentView->m_rModuleList.InsertPrev(pViewRef);
    }

    if (pStackTop) {
        PUSH_ARG(*pStackTop, pCurrentView->m_pModule->m_uImageBase);
        PUSH_ARG(*pStackTop, pCurrentView->m_pModule->m_uEntryPoint);
    }
    else {
        pDllEntries->Append((Address*)&pCurrentView->m_pModule->m_uEntryPoint,
            1);
        pDllEntries->Append((Address*)&pCurrentView->m_pModule->m_uImageBase,
            1);
    }

    *ppModuleView = pCurrentView;
    pCurrentView->AddRef();
    return NOERROR;

ErrorExit:
    pCurrentView->Detach();
    delete pCurrentView;
    return ec;
}

ECode CProcess::MapModule(CModule * pModule)
{
    register UInt32 n;
    UInt32 uAddr, dwFlags;
    ECode ec;

    for (n = 0; n < pModule->m_cSections; n++) {
        if (pModule->m_pSections[n].m_dwProtect & MemoryProtection_Write) {
            dwFlags = MemoryMap_Fixed | MemoryMap_Private;
        }
        else {
            dwFlags = MemoryMap_Fixed | MemoryMap_Shared;
        }

        ec = m_AS.Map(
            pModule->m_uImageBase + pModule->m_pSections[n].m_uVAddr,
            pModule->m_pSections[n].m_uVSize,
            dwFlags,
            pModule->m_pSections[n].m_dwProtect,
            &(pModule->m_pSections[n]),
            0,
            &uAddr);
        if (FAILED(ec)) {
            // unmap those had mapped sections.
            //
            for (; n > 0; n--) {
                m_AS.Unmap(
                    pModule->m_uImageBase + pModule->m_pSections[n-1].m_uVAddr,
                    pModule->m_pSections[n - 1].m_uVSize);
            }

            return ec;
        }
    }

    return NOERROR;
}
#ifdef DEBUG_KMUTEX
static KMutex s_pageFaultLock(__FILE__, __LINE__);
#else
static KMutex s_pageFaultLock;
#endif //DEBUG_KMUTEX
ECode CProcess::PageFault(
    UInt32 uAddr, UInt32 uStackPoint, Boolean bIsKernel, Boolean bCheckWritable)
{
    Thread *pCurThd;
    UInt32 uStackBase;
    UInt32 uStackSize;
    ECode ec;

#ifdef _DEBUG
    if (!PreemptionEnabled()) {
        kprintf("%s, %d\n", __FILE__, __LINE__);
        return E_FAIL;
    }
#endif

    s_pageFaultLock.Lock();

    if (bIsKernel) {
        ec = m_AS.Hit(uAddr);
        s_pageFaultLock.Unlock();
        return ec;
    }

    if (bCheckWritable) {
        Boolean bIsValid;
        ec = m_AS.CheckProtect(uAddr, MemoryProtection_Write, &bIsValid);
        if (FAILED(ec) || !bIsValid) {
            s_pageFaultLock.Unlock();
            return E_FAIL;
        }
    }
    Boolean bIsAutoCommit;
    ec = m_AS.IsAutoCommit(uAddr, &bIsAutoCommit);
    if (FAILED(ec) || !bIsAutoCommit) {
        s_pageFaultLock.Unlock();
        return E_FAIL;
    }

    pCurThd = GetCurrentThread();
    if (!pCurThd) {
        s_pageFaultLock.Unlock();
        return E_FAIL;
    }

    assert(pCurThd->m_uUserStackBase);
    uStackBase = pCurThd->m_uUserStackBase;
    uStackSize =  USER_STACK_SIZE;

    // Check user stack
    //
    if (!FALL_INTO(uStackPoint, uStackBase + STACK_GROW_MAX_SIZE,
            USER_STACK_SIZE - STACK_GROW_MAX_SIZE)) {
        // User stack grow up to USER_STACK_SIZE
        //
        ec = m_AS.Query(uStackPoint, &uStackBase, (MemorySize *)&uStackSize);
        if (NOERROR != ec) {
            kprintf("ERROR! Can't grow user stack.\n");
            s_pageFaultLock.Unlock();
            return E_NOT_ENOUGH_ADDRESS_SPACE;
        }
    }

    //
    // Check if is stack addr page fault
    //
    if (FALL_INTO(uAddr, uStackPoint - STACK_GROW_MAX_SIZE, \
        (uStackBase + uStackSize) - (uStackPoint - STACK_GROW_MAX_SIZE))) {
        // Commit physical memory for user stack
        //
        ec = m_AS.Hit(uStackPoint - STACK_GROW_MAX_SIZE);
        s_pageFaultLock.Unlock();
        return ec;
    }
    else {
        // Other segment page fault (not stack)
        //
        ec = m_AS.Hit(uAddr);
        s_pageFaultLock.Unlock();
        return ec;
    }
}

#define ALL_SYSTEM_EVENTS \
        (SystemEvent_Break | SystemEvent_SessionEnd  \
            | SystemEvent_TimeChanged | SystemEvent_LowMemory \
            | SystemEvent_CriticallyLowMemory | SystemEvent_Shutdown)

extern "C" Boolean g_bIsLowMemory;
extern "C" Boolean g_bIsCriticallyLowMemory;

ECode CProcess::RequestSystemEvent(
    SystemEvents dwInterestedEvents, IEvent **ppEvent)
{
    Boolean bManualReset = FALSE;
    CSystemEvent *tempEvent;

    if (NULL == ppEvent || !dwInterestedEvents
        || ALL_SYSTEM_EVENTS != (dwInterestedEvents | ALL_SYSTEM_EVENTS)) {
        return E_INVALID_ARGUMENT;
    }

    if (dwInterestedEvents & SystemEvent_LowMemory) {
        if (dwInterestedEvents == SystemEvent_LowMemory) {
            bManualReset = TRUE;
        }
        else {
            // Manual reset event conflict with other system events
            return E_INVALID_ARGUMENT;
        }
    }

    if (dwInterestedEvents & SystemEvent_CriticallyLowMemory) {
        if (dwInterestedEvents == SystemEvent_CriticallyLowMemory) {
            bManualReset = TRUE;
        }
        else {
            // Manual reset event conflict with other system events
            return E_INVALID_ARGUMENT;
        }
    }

    tempEvent = CreateSystemEvent(this, bManualReset);
    if (NULL == tempEvent) return E_OUT_OF_MEMORY;

    tempEvent->Interested(dwInterestedEvents);

    if (dwInterestedEvents & SystemEvent_LowMemory
        && dwInterestedEvents == SystemEvent_LowMemory
        && g_bIsLowMemory) {
        tempEvent->Trigger(SystemEvent_LowMemory);
    }

    if (dwInterestedEvents & SystemEvent_CriticallyLowMemory
        && dwInterestedEvents == SystemEvent_CriticallyLowMemory
        && g_bIsLowMemory) {
        tempEvent->Trigger(SystemEvent_CriticallyLowMemory);
    }

    *ppEvent = tempEvent;
    return NOERROR;
}

void CProcess::UnmapAndDeleteModView(ModuleView *pDelModView)
{
    ModuleViewRef *pModViewRef;
    ModuleView    *pModView;
    UInt32 n;
    Boolean bIsMaster;
    Thread *pThread;
    UInt32 uTempStack;

    pThread = NULL;
    uTempStack = 0;

    m_deleteModLock.Lock();

    bIsMaster = m_deleteModList.IsEmpty();

    // GetThread, ready to call _DllMainCRTStartup
    //
    if (bIsMaster) {
        if (m_bIsCallDllDetach && SUCCEEDED(GetThread(&pThread))) {
            assert(pThread->m_uUserStackBase);
            m_uStackTop = pThread->m_uUserStackBase \
                + USER_STACK_SIZE - USER_STACK_PRESERVED_SIZE;

            memset((void *)(
                m_uStackTop + USER_STACK_PRESERVED_SIZE - TLS_SIZE),
                0, TLS_SIZE);

            uTempStack = m_uStackTop;
        }
        else {
            // GetThread failed or no need to call dll detach.
            pThread = NULL;
            m_uStackTop = 0;
        }
    }

    // Only call DLL entry here. Don't call EXE entry
    // AND must GetThread successed
    //
    if (!FALL_INTO(pDelModView->m_pModule->m_uImageBase, \
        EXE_IMAGE_BASE, EXE_IMAGE_SIZE) && m_uStackTop) {
        PUSH_ARG(m_uStackTop, pDelModView->m_pModule->m_uImageBase);
        PUSH_ARG(m_uStackTop, pDelModView->m_pModule->m_uEntryPoint);
    }

    // Detach this ModuleView from module list of current process,
    // AND Insert to s_deleteModList.
    //
    pModViewRef = new ModuleViewRef(pDelModView);
    if (NULL == pModViewRef) return;
    m_deleteModList.InsertFirst(pModViewRef);

    // Release dependence ModuleView
    // It may reenter this function
    //
    pModViewRef = (ModuleViewRef *)pDelModView->m_rModuleList.m_pNext;
    while ((DLinkNode *)pModViewRef != &pDelModView->m_rModuleList) {
        pModViewRef->Detach();
        pModViewRef->m_pModuleView->Release();
        delete pModViewRef;
        pModViewRef = (ModuleViewRef *)pDelModView->m_rModuleList.m_pNext;
    }

    if (bIsMaster) {
        if (pThread && m_bIsCallDllDetach) {
            PUSH_ARG(m_uStackTop, NULL);               // end of
                                                       // startup entry
            PUSH_ARG(m_uStackTop, DLL_PROCESS_DETACH); // dwReason
            // point to first startup entry
            PUSH_ARG(m_uStackTop, uTempStack - 8);
            m_uStackTop -= 4;                         // skip return address

            // Call _DllMainCRTStartup with DLL_PROCESS_DETACH,
            // AND wait for exit.
            //
            pThread->SetUserEntry(
                (void *)m_uStackTop,
                (void *)USER_STARTUP_ENTRY_BASE);
            UInt32 handle[] = {(UInt32)pThread, USER_STARTUP_ENTRY_BASE};
            TryToSendReceiveDebugMsg(DEBUGGEE_THREAD_CREATE, (UInt32)handle);

            pThread->m_isDllProcDetachThd = TRUE;
            pThread->Start();
            WaitResult wr;
            pThread->m_joinEvent.TryUninterruptibleWait(300, &wr, NULL);
            pThread->Abort(this);
            pThread->m_uPoolThreadState = ThreadState_Finished;
            // Release twice, one for SysExitThread, one for this
            // local variable
            //
            pThread->Release();
            pThread->Release();
            m_uStackTop = 0;
        }

        // Unmap AND delete all module (include pDelModView Module)
        // in s_deleteModList
        //
        pModViewRef = (ModuleViewRef *)m_deleteModList.m_pNext;
        while ((DLinkNode *)pModViewRef != &m_deleteModList) {
            pModView = pModViewRef->m_pModuleView;
            // Unmap module from process
            //
            for (n = 0; n < pModView->m_pModule->m_cSections; n++) {
                m_AS.Unmap(
                    pModView->m_pModule->m_uImageBase
                        + pModView->m_pModule->m_pSections[n].m_uVAddr,
                    pModView->m_pModule->m_pSections[n].m_uVSize);
            }
            pModViewRef->Detach();
            DzDisablePreemption();
            if (m_moduleLock.IsHeld() || !m_moduleLock.HasOwner()) {
                pModViewRef->m_pModuleView->Detach();
                m_nModuleListState++;
                EnablePreemption();
                delete pModViewRef->m_pModuleView;
            }
            else {
                EnablePreemption();
                pModViewRef->m_pModuleView->m_modviewstate \
                        = ModViewState_Deleted;
            }
            delete pModViewRef;
            pModViewRef = (ModuleViewRef *)m_deleteModList.m_pNext;
        }
        assert(m_deleteModList.IsEmpty());
    }
    m_deleteModLock.Unlock();
}

void CProcess::FreeProcess()
{
    Thread *pThread;

    m_threadPool.m_threadLock.Lock();
    while (!m_threadPool.m_threadList.IsEmpty()) {
        pThread = THIS(
            (InProcessLinkNode *)m_threadPool.m_threadList.m_pNext,
            Thread, m_inProcLink);
        pThread->m_inProcLink.Detach();
        // for detach twice.(In thread release)
        pThread->m_inProcLink.Initialize();
        assert(ThreadState_Running != pThread->m_uPoolThreadState);
        pThread->Abort(this);
    }
    m_threadPool.m_threadLock.Unlock();

    m_eventListLock.Lock();
    while (!m_eventList.IsEmpty()) {
        register EventNode *pEventNode = (EventNode *)m_eventList.First();
        pEventNode->Detach();
        delete pEventNode;
    }
    m_eventListLock.Unlock();

    DzFreeKernelPages(m_vaBlock, GET_UP_ORDER(USER_BLOCK_SIZE / PAGE_SIZE));

#ifndef _RELEASE
    g_bDbgStubEntry = FALSE; //for gdbstub
#endif //_RELEASE

    CProcess::s_processListLock.Lock();
    this->Detach();
    CProcess::s_nProcessListState++;
    CProcess::s_processListLock.Unlock();
    this->Release();

    m_exitEvent.Notify();
}

ECode CProcess::CreateEvent(EventNode **ppEventNode)
{
    m_eventListLock.Lock();

    EventNode *pEventNode = new EventNode;
    if (NULL == pEventNode) {
        m_eventListLock.Unlock();
        return E_OUT_OF_MEMORY;
    }

    m_eventList.InsertLast(pEventNode);
    m_eventListLock.Unlock();

    *ppEventNode = pEventNode;

    return NOERROR;
}

ECode CProcess::DestroyEvent(EventNode *pEventNode)
{
    m_eventListLock.Lock();

    if (!IsValidEventNode(pEventNode)) {
        m_eventListLock.Unlock();
        return E_INVALID_ARGUMENT;
    }

    pEventNode->Detach();
    delete pEventNode;

    m_eventListLock.Unlock();

    return NOERROR;
}

ECode CProcess::NotifyEvent(EventNode *pEventNode)
{
    m_eventListLock.Lock();

    if (!IsValidEventNode(pEventNode)) {
        m_eventListLock.Unlock();
        return E_INVALID_ARGUMENT;
    }

    m_eventListLock.Unlock();

    pEventNode->m_event.Notify(TRUE);

    return NOERROR;
}

ECode CProcess::WaitEvent(EventNode *pEventNode)
{
    m_eventListLock.Lock();

    if (!IsValidEventNode(pEventNode)) {
        m_eventListLock.Unlock();
        return E_INVALID_ARGUMENT;
    }

    m_eventListLock.Unlock();

    WaitResult wr;
    pEventNode->m_event.Wait(&wr, NULL);
    return NOERROR;
}

ECode CProcess::AcquireThreadLocalSlot(
    /* [in] */ ThreadLocalSlotType slotType,
    /* [out] */ Int32 *pSlotIndex)
{
    switch (slotType) {
        case ThreadLocalSlotType_Value:
        case ThreadLocalSlotType_Mem:
        case ThreadLocalSlotType_Obj:
            break;
        default:
            return E_INVALID_ARGUMENT;
    }

    if (NULL == pSlotIndex) return E_INVALID_ARGUMENT;

    m_threadLock.Lock();

    for (uint_t slotIndex = 0; slotIndex < TLS_MINIMUM_AVAILABLE;
            slotIndex++) {
        if (ThreadLocalSlotType_Free == m_tlsTypes[slotIndex]) {
            m_tlsTypes[slotIndex] = slotType;
            m_threadLock.Unlock();

            *pSlotIndex = slotIndex;
            return NOERROR;
        }
    }

    m_threadLock.Unlock();

    return E_OUT_OF_MEMORY;
}

ECode CProcess::RelinquishThreadLocalSlot(
    /* [in] */ Int32 slotIndex,
    /* [out] */ BufferOf<Address> * pUsedPtrs)
{
    if (pUsedPtrs == NULL ||
        slotIndex < 0 || slotIndex >= TLS_MINIMUM_AVAILABLE) {
        return E_INVALID_ARGUMENT;
    }

    m_threadLock.Lock();

    if (ThreadLocalSlotType_Free == m_tlsTypes[slotIndex]) {
        m_threadLock.Unlock();
        return E_INVALID_ARGUMENT;
    }

    assert(ThreadLocalSlotType_Value == m_tlsTypes[slotIndex]
        || ThreadLocalSlotType_Mem == m_tlsTypes[slotIndex]
        || ThreadLocalSlotType_Obj == m_tlsTypes[slotIndex]);

    pUsedPtrs->SetUsed(0);

    int i = 0;

    InProcessLinkNode *pThreadNode;
    ForEachDLinkNode(InProcessLinkNode *, pThreadNode, &m_threadList) {
        Thread *pThread = THIS(pThreadNode, Thread, m_inProcLink);
        bool_t bOriginalPreemption = DzDisablePreemption();
        if (0 != pThread->m_uUserStackBase) {
            if (ThreadLocalSlotType_Value != m_tlsTypes[slotIndex]) {
                pUsedPtrs->SetUsed(i + 1);
                (*pUsedPtrs)[i] = (UInt32)
                    (((PVoid *)(pThread->m_uUserStackBase + USER_STACK_SIZE))[
                        (int)slotIndex - TLS_TOTAL_SLOTS]);
                i++;
            }

            ((PVoid *)(pThread->m_uUserStackBase + USER_STACK_SIZE))[
                (int)slotIndex - TLS_TOTAL_SLOTS] = NULL;
        }
        DzRestorePreemption(bOriginalPreemption);
    }

    m_tlsTypes[slotIndex] = ThreadLocalSlotType_Free;

    m_threadLock.Unlock();

    return NOERROR;
}

ECode CProcess::CleanUpThreadLocalSlot(
    /* [in] */ Int32 slotIndex,
    /* [out] */ BufferOf<Address> * pUsedPtrs)
{
    if (pUsedPtrs == NULL ||
        slotIndex < 0 || slotIndex >= TLS_TOTAL_SLOTS) {
        return E_INVALID_ARGUMENT;
    }

    m_threadLock.Lock();

    if (ThreadLocalSlotType_Free == m_tlsTypes[slotIndex]) {
        m_threadLock.Unlock();
        return E_INVALID_ARGUMENT;
    }

    assert(ThreadLocalSlotType_Value == m_tlsTypes[slotIndex]
        || ThreadLocalSlotType_Mem == m_tlsTypes[slotIndex]
        || ThreadLocalSlotType_Obj == m_tlsTypes[slotIndex]);

    pUsedPtrs->SetUsed(0);

    int i = 0;

    InProcessLinkNode *pThreadNode;
    ForEachDLinkNode(InProcessLinkNode *, pThreadNode, &m_threadList) {
        Thread *pThread = THIS(pThreadNode, Thread, m_inProcLink);
        bool_t bOriginalPreemption = DzDisablePreemption();
        if (0 != pThread->m_uUserStackBase) {
            if (ThreadLocalSlotType_Value != m_tlsTypes[slotIndex]) {
                pUsedPtrs->SetUsed(i + 1);
                (*pUsedPtrs)[i] = (UInt32)
                    (((PVoid *)(pThread->m_uUserStackBase + USER_STACK_SIZE))[
                        (int)slotIndex - TLS_TOTAL_SLOTS]);
                i++;
            }

            ((PVoid *)(pThread->m_uUserStackBase + USER_STACK_SIZE))[
                (int)slotIndex - TLS_TOTAL_SLOTS] = NULL;
        }
        DzRestorePreemption(bOriginalPreemption);
    }

    m_threadLock.Unlock();

    return NOERROR;
}

EXTERN_C IMutex *EnvironMutex()
{
    assert(GetCurrentProcess());
    return (IMutex *)&GetCurrentProcess()->m_envMutex;
}
